﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using DevelopTool.Bll.LocalDbBlls;
using DevelopTool.Common;
using DevelopTool.Model.CommonModels;
using DevelopTool.Model.Enums;
using DevelopTool.Model.OffLineModels;
using DevelopTool.Model.RemoteModels;
using LiteDB;

namespace DevelopTool.Bll.RemoteBlls
{
    public class RemoteBll : BaseBll
    {

        public RemoteBll()
        {

            using(var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<Remote>();
                col.EnsureIndex(x => x.NodeName);
                col.EnsureIndex(x => x.NodePid);
                col.EnsureIndex(x => x.NodeType);
                col.EnsureIndex(x => x.CreateTime);
                col.EnsureIndex(x => x.Id);
            }
        }

        public List<Remote> GetList(MenuNodeType nodeType = MenuNodeType.Unknown)
        {
            using(var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var models = db.GetCollection<Remote>().FindAll();

                if(nodeType != MenuNodeType.Unknown)
                {
                    models = models.Where(it => it.NodeType == nodeType);
                }

                return models.OrderByDescending(it => it.CreateTime).ToList();
            }
        }

        public Remote GetById(int id)
        {
            using(var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<Remote>();
                var remote = col.FindOne(it => it.Id == id);

                //解压缩内容
                if(remote != null && remote.RdpConfigsByte != null && remote.RdpConfigsByte.Count() > 0)
                    remote.RdpConfigs = remote.RdpConfigsByte.ByteToObject_Decompress<List<RdpConfig>>();

                return remote;

            }
        }


        public Remote Save(Remote remote)
        {
            using(var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<Remote>();

                //压缩内容
                if(remote.RdpConfigs != null && remote.RdpConfigs.Count > 0)
                    remote.RdpConfigsByte = remote.RdpConfigs.ObjectToByte_Compress();

                remote.UpdateTime = DateTime.Now;
                col.Upsert(remote);

                return remote;
            }
        }

        public bool Delete(int id)
        {
            if(id <= 0) return false;

            using(var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var remoteCol = db.GetCollection<Remote>();

                //查询是否有子节点，若有子节点，则不删除。
                if(remoteCol.Exists(it => it.NodePid == id)) return false;

                //删除
                var result = remoteCol.Delete(id);

                return result;
            }
        }


        /// <summary>
        /// rdp连接
        /// </summary>
        /// <param name="remote"></param>
        public void ConnectRdp(Remote remote)
        {
            string filePath = $"{GlobalData.AppTempDirectory}{remote.NodeName}.rdp";
            CreateRdpFile(remote.RdpConfigs,filePath);

            Process.Start("mstsc.exe",filePath);
        }


        /// <summary>
        /// 保存到文件
        /// </summary>
        /// <param name="filePath"></param>
        public void SaveAsToFile(Remote remote,string filePath)
        {
            CreateRdpFile(remote.RdpConfigs,filePath);
        }


        /// <summary>
        /// 创建rdp文件
        /// </summary>
        /// <param name="rdpConfigs"></param>
        /// <param name="filePath"></param>
        private void CreateRdpFile(List<RdpConfig> rdpConfigs,string filePath)
        {
            if(File.Exists(filePath)) File.Delete(filePath);

            using(StreamWriter streamWriter = new StreamWriter(filePath,true))
            {
                foreach(RdpConfig rdpConfig in rdpConfigs)
                {
                    if(rdpConfig.Key == "password 51:b")
                    {
                        rdpConfig.Value = RDPHelper.Encrypt(rdpConfig.Value);
                    }

                    streamWriter.WriteLine($"{rdpConfig.Key}:{rdpConfig.Value}");
                }
            }
        }

        /// <summary>
        /// rdp默认配置
        /// </summary>
        public List<Tuple<string,string,bool,string,string>> DefaultRdpConfigs = new List<Tuple<string,string,bool,string,string>>()
        {
            new Tuple<string, string, bool,string,string>("full address:s","",false,"连接信息","'电脑名称：此设置指定要连接到的远程计算机的名称或 IP 地址。这是 RDP 文件唯一必需的设置。'有效的名称、IPv4 地址或 IPv6 地址。"),
            new Tuple<string, string, bool,string,string>("alternate full address:s","",true,"连接信息","指定远程计算机的备用名称或 IP 地址。有效的名称、IPv4 地址或 IPv6 地址。"),
            new Tuple<string, string, bool,string,string>("username:s","",true,"连接信息","指定用于登录远程计算机的用户帐户的名称。任何有效的用户名。"),
            new Tuple<string, string, bool,string,string>("password 51:b","",true,"连接信息","远程计算机登录密码"),
            new Tuple<string, string, bool,string,string>("domain:s","",true,"连接信息","指定用于登录远程计算机的用户帐户所在域的名称。有效的域名（如“CONTOSO”）。"),
            new Tuple<string, string, bool,string,string>("gatewayhostname:s","",true,"连接信息","指定 RD 网关主机名。有效的名称、IPv4 地址或 IPv6 地址。"),
            new Tuple<string, string, bool,string,string>("gatewaycredentialssource:i","0",true,"连接信息","指定 RD 网关身份验证方法。'- 0：请求密码 (NTLM)- 1：使用智能卡- 2：使用当前登录用户的凭据。- 3：提示用户输入其凭据并使用基本身份验证- 4：允许用户稍后选择- 5：使用基于 Cookie 的身份验证'"),
            new Tuple<string, string, bool,string,string>("gatewayprofileusagemethod:i","0",true,"连接信息","指定是否使用默认 RD 网关设置。'- 0：使用管理员指定的默认配置文件模式- 1：使用用户指定的显式设置'"),
            new Tuple<string, string, bool,string,string>("gatewayusagemethod:i","0",true,"连接信息","指定何时将 RD 网关用于连接。'- 0：不使用 RD 网关- 1：始终使用 RD 网关- 2：在无法与 RD 会话主机建立直接连接的情况下使用 RD 网关- 3：使用默认 RD 网关设置- 4：不要使用 RD 网关，针对本地地址绕过网关将此属性值设置为 0 或 4 实际上等效，但将此属性设置为 4 可使该选项绕过本地地址。'"),
            new Tuple<string, string, bool,string,string>("promptcredentialonce:i","0",true,"连接信息","确定是否保存用户的凭据并将其用于 RD 网关和远程计算机。'- 0：远程会话不使用相同的凭据- 1：远程会话将使用相同的凭据'"),
            new Tuple<string, string, bool,string,string>("authentication level:i","3",true,"连接信息","定义服务器身份验证级别设置。'- 0：如果服务器身份验证失败，请在不显示警告的情况下连接到计算机（连接且不向我发出警告）- 1：如果服务器身份验证失败，请不要建立连接（不要连接）- 2：如果服务器身份验证失败，请显示警告并允许我连接或拒绝连接（向我发出警告）- 3：未指定身份验证要求。'"),
            new Tuple<string, string, bool,string,string>("enablecredsspsupport:i","1",true,"连接信息","确定客户端是否会在凭据安全支持提供程序 (CredSSP) 可用的情况下使用它来进行身份验证。'- 0：即使操作系统支持 CredSSP，RDP 也不会使用 CredSSP- 1：如果操作系统支持 CredSSP，则 RDP 将使用 CredSSP'"),
            new Tuple<string, string, bool,string,string>("enablerdsaadauth:i","0",true,"连接信息","确定客户端是否将使用 Azure AD 向远程电脑进行身份验证（如果可用）。'- 0：即使远程电脑支持 Azure AD 身份验证，RDP 也不会使用该身份验证-1：如果远程电脑支持 Azure AD 身份验证，RDP 将使用该身份验证'"),
            new Tuple<string, string, bool,string,string>("disableconnectionsharing:i","0",true,"连接信息","确定客户端在新连接启动时是重新连接到任何已断开连接的现有会话，还是启动新连接。'- 0：重新连接到任何现有会话- 1：启动新连接'"),
            new Tuple<string, string, bool,string,string>("alternate shell:s","",true,"连接信息","指定要在远程会话中作为 shell（而不是资源管理器）自动启动的程序。指向可执行文件的有效路径（如“C:\\ProgramFiles\\Office\\word.exe”）"),

            new Tuple<string, string, bool,string,string>("autoreconnection enabled:i","1",true,"会话行为","确定客户端是否会在连接断开时（例如，当网络连接中断时）自动尝试重新连接到远程计算机。'- 0：客户端不会自动尝试重新连接- 1：客户端会自动尝试重新连接'"),
            new Tuple<string, string, bool,string,string>("bandwidthautodetect:i","1",true,"会话行为","确定是否使用自动网络带宽检测。 需要将 bandwidthautodetect 设置为 1。'- 0：禁用自动网络类型检测- 1：启用自动网络类型检测'"),
            new Tuple<string, string, bool,string,string>("networkautodetect:i","1",true,"会话行为","确定是否启用了自动网络类型检测'- 0：不使用自动网络带宽检测- 1：使用自动网络带宽检测'"),
            new Tuple<string, string, bool,string,string>("compression:i","1",true,"会话行为","确定在通过 RDP 传输到本地计算机时是否启用批量压缩。'- 0：禁用 RDP 批量压缩- 1：启用 RDP 批量压缩'"),
            new Tuple<string, string, bool,string,string>("videoplaybackmode:i","1",true,"会话行为","确定连接是否会使用 RDP 高效多媒体流式处理进行视频播放。'-0：不使用 RDP 高效多媒体流式处理进行视频播放- 1：在可能的情况下使用 RDP 高效多媒体流式处理进行视频播放'"),

            new Tuple<string, string, bool,string,string>("audiocapturemode:i","0",true,"设备重定向","'麦克风重定向：指明是否已启用音频输入重定向。''- 0：禁用本地设备的音频捕获- 1：启用本地设备的音频捕获并重定向到远程会话中的音频应用程序'"),
            new Tuple<string, string, bool,string,string>("encode redirected video capture:i","1",true,"设备重定向","启用或禁用已重定向视频的编码。'-0：禁用重定向视频的编码- 1：启用重定向视频的编码'"),
            new Tuple<string, string, bool,string,string>("redirected video capture encoding quality:i","0",true,"设备重定向","控制已编码视频的质量。'- 0：高压缩视频。 当有大量的运动时，质量可能会受到影响。- 1：中压缩。- 2：低压缩视频，图片质量高。'"),
            new Tuple<string, string, bool,string,string>("audiomode:i","0",true,"设备重定向","'音频输出位置：确定本地或远程计算机是否播放音频。''-0：在本地计算机上播放音频（在此计算机上播放）-1：在远程计算机上播放音频（在远程计算机上播放）-2：不播放声音（不要播放）'"),
            new Tuple<string, string, bool,string,string>("camerastoredirect:s","",true,"设备重定向","'摄像头重定向：配置要重定向哪些摄像头。 此设置使用分号分隔的列表，其中包含支持重定向的摄像头的 KSCATEGORY_VIDEO_CAMERA 接口。''- *：重定向所有摄像头- 摄像头列表，例如 camerastoredirect:s:\\?\\usb#vid_0bda&pid_58b0&mi- - 可以通过在符号链接字符串前面加上“-”来排除特定摄像头'"),
            new Tuple<string, string, bool,string,string>("devicestoredirect:s","",true,"设备重定向","'即插即用设备重定向：确定本地计算机上将重定向并在远程会话中可用的设备。''-*：重定向所有受支持的设备，包括稍后连接的设备- - 一个或多个设备的有效硬件 ID- DynamicDevices：重定向稍后将连接的所有受支持的设备'"),
            new Tuple<string, string, bool,string,string>("drivestoredirect:s","",true,"设备重定向","'驱动器/存储重定向：确定本地计算机上将重定向并在远程会话中可用的磁盘驱动器。''- 未指定值：不重定向任何驱动器- *：重定向所有磁盘驱动器，包括稍后连接的驱动器- DynamicDrives：重定向稍后连接的所有驱动器- 驱动器以及一个或多个驱动器的标签，例如“drivestoredirect:s:C:;E:;”：重定向指定的驱动器'"),
            new Tuple<string, string, bool,string,string>("keyboardhook:i","2",true,"设备重定向","确定何时将 Windows 键组合（Windows 徽标键、Alt+Tab）应用到桌面和 RemoteApp 连接的远程会话。'- 0：在本地计算机上应用 Windows 组合键- 1：（仅限桌面）对焦时，在远程计算机上应用 Windows 组合键- 2：（仅限桌面）仅在全屏模式下，在远程计算机上应用 Windows 组合键- 3：（仅限 RemoteApp）在对焦时，在 RemoteApp 上应用 Windows 组合键。'"),
            new Tuple<string, string, bool,string,string>("redirectclipboard:i","1",true,"设备重定向","'剪贴板重定向：确定是否已启用剪贴板重定向。''-0：本地计算机上的剪贴板在远程会话中不可用- 1：本地计算机上的剪贴板在远程会话中可用'"),
            new Tuple<string, string, bool,string,string>("redirectcomports:i","0",true,"设备重定向","'COM 端口重定向：确定本地计算机上的 COM（串行）端口是否会被重定向并在远程会话中可用。''-0：本地计算机上的 COM 端口在远程会话中不可用-1：本地计算机上的 COM 端口在远程会话中可用'"),
            new Tuple<string, string, bool,string,string>("redirectlocation:i","0",true,"设备重定向","'位置重定向：确定本地设备的位置是否会被重定向并在远程会话中可用''- 0：远程会话使用远程计算机或虚拟机的位置- 1：远程会话使用本地设备的位置'"),
            new Tuple<string, string, bool,string,string>("redirectprinters:i","1",true,"设备重定向","'打印机重定向：确定本地计算机上配置的打印机是否会被重定向并在远程会话中可用''-0：本地计算机上的打印机在远程会话中不可用-1：本地计算机上的打印机在远程会话中可用'"),
            new Tuple<string, string, bool,string,string>("redirectsmartcards:i","1",true,"设备重定向","'智能卡重定向：确定本地计算机上的智能卡设备是否会被重定向并在远程会话中可用。''-0：本地计算机上的智能卡设备在远程会话中不可用- 1：本地计算机上的智能卡设备在远程会话中可用'"),
            new Tuple<string, string, bool,string,string>("redirectwebauthn:i","1",true,"设备重定向","'WebAuthn 重定向：确定远程计算机上的 WebAuthn 请求是否将被重定向到允许使用本地身份验证器（例如 Windows Hello for Business、安全密钥等）的本地计算机。''- 0：来自远程会话的 WebAuthn 请求不会发送到本地计算机进行身份验证，必须在远程会话中完成- 1：来自远程会话的 WebAuthn 请求将被发送到本地计算机进行身份验证'"),
            new Tuple<string, string, bool,string,string>("usbdevicestoredirect:s","",true,"设备重定向","USB 重定向'- *：重定向还没有被另一个高级重定向重定向的所有 USB 设备- {设备安装程序类 GUID}：重定向由实例 ID 标识的特定 USB 设备设备安装程序类- USBInstanceID：重定向由实例 ID 标识的特定 USB 设备'"),

            new Tuple<string, string, bool,string,string>("use multimon:i","0",true,"显示设置","确定远程会话是否会使用本地计算机中的一个或多个显示。'- 0：不启用多显示支持- 1：启用多显示支持'"),
            new Tuple<string, string, bool,string,string>("selectedmonitors:s","",true,"显示设置","'指定要在远程会话中使用的本地显示。 所选的显示必须是连续的。 要求使用 multimon 设置为 1。仅在 Windows 收件箱中 (MSTSC) 和 Windows 桌面 (MSRDC) 客户端中可用。'计算机特定的显示 ID 的列表（用逗号分隔）。 可以通过调用 mstsc.exe /l 来检索 ID。 列出的第一个 ID 将被设置为会话中的主显示。"),
            new Tuple<string, string, bool,string,string>("maximizetocurrentdisplays:i","0",true,"显示设置","'确定远程会话在最大化时在哪个显示中进入全屏。 要求使用 multimon 设置为 1。仅在 Windows 桌面 (MSRDC) 客户端中可用。''- 0：会话在最大化时在最初选择的显示器上全屏显示- 1：会话在最大化时在会话窗口所触及的显示器上动态地全屏显示'"),
            new Tuple<string, string, bool,string,string>("singlemoninwindowedmode:i","0",true,"显示设置","'确定多显示远程会话在退出全屏时是否自动切换为单个显示。 要求使用 multimon 设置为 1。仅在 Windows 桌面 (MSRDC) 客户端中可用。''- 0：会话在退出全屏时保留所有显示- 1：会话在退出全屏时切换为单个显示'"),
            new Tuple<string, string, bool,string,string>("screen mode id:i","2",true,"显示设置","确定远程会话窗口是否在启动连接时全屏显示。'- 1：远程会话将在窗口中显示- 2：远程会话将全屏显示'"),
            new Tuple<string, string, bool,string,string>("smart sizing:i","0",true,"显示设置","确定本地计算机是否缩放远程会话的内容来适应窗口大小。'- 0：本地窗口内容在调整大小时不会缩放- 1：本地窗口内容在调整大小时缩放'"),
            new Tuple<string, string, bool,string,string>("dynamic resolution:i","1",true,"显示设置","确定在调整本地窗口大小时是否自动更新远程会话的分辨率。'- 0：会话分辨率在会话期间保持静态- 1：当本地窗口调整大小时，会话分辨率随之更新'"),
            new Tuple<string, string, bool,string,string>("desktop size id:i","",true,"显示设置","根据一组预定义选项指定远程会话桌面的尺寸。 如果指定了 desktopheight 和 desktopwidth，则会替代此设置。'-0: 640×480- 1: 800×600- 2: 1024×768- 3: 1280×1024- 4: 1600×1200'"),
            new Tuple<string, string, bool,string,string>("desktopheight:i","",true,"显示设置","指定远程会话的分辨率高度（以像素为单位）。介于 200 和 8192 之间的数值"),
            new Tuple<string, string, bool,string,string>("desktopwidth:i","",true,"显示设置","指定远程会话的分辨率宽度（以像素为单位）。介于 200 和 8192 之间的数值"),
            new Tuple<string, string, bool,string,string>("desktopscalefactor:i","",true,"显示设置","指定远程会话的比例因子，让内容看起来更大。以下列表中的数值：100, 125, 150, 175, 200, 250, 300, 400, 500"),

            //new Tuple<string, string, bool,string,string>("remoteapplicationcmdline:s","",true,"RemoteApp","RemoteApp 的可选命令行参数。有效的命令行参数。"),
            //new Tuple<string, string, bool,string,string>("remoteapplicationexpandcmdline:i","",true,"RemoteApp","确定 RemoteApp 命令行参数中包含的环境变量应该在本地扩展还是远程扩展。'- 0：应将环境变量扩展为本地计算机的值- 1：应将环境变量扩展为远程计算机的值'"),
            //new Tuple<string, string, bool,string,string>("remoteapplicationexpandworkingdir:i","",true,"RemoteApp","确定 RemoteApp 工作目录参数中包含的环境变量应该在本地扩展还是远程扩展。'- 0：应将环境变量扩展为本地计算机的值- 1：应将环境变量扩展为远程计算机的值。RemoteApp 工作目录通过 shell 工作目录参数指定。'"),
            //new Tuple<string, string, bool,string,string>("remoteapplicationfile:s","",true,"RemoteApp","'指定 RemoteApp 要在远程计算机上打开的文件。若要打开本地文件，还必须为源驱动器启用驱动器重定向。'有效文件路径。"),
            //new Tuple<string, string, bool,string,string>("remoteapplicationicon:s","",true,"RemoteApp","指定在启动 RemoteApp 时要在客户端 UI 中显示的图标文件。 如果未指定文件名，则客户端将使用标准远程桌面图标。 仅支持“.ico”文件。有效文件路径。"),
            //new Tuple<string, string, bool,string,string>("remoteapplicationmode:i","",true,"RemoteApp","确定是否将连接作为 RemoteApp 会话启动。'- 0：不启动 RemoteApp 会话- 1：启动 RemoteApp 会话'"),
            //new Tuple<string, string, bool,string,string>("remoteapplicationname:s","",true,"RemoteApp","指定启动 RemoteApp 时客户端界面中的 RemoteApp 名称。应用显示名称。 例如，“Excel 2016”。"),
            //new Tuple<string, string, bool,string,string>("remoteapplicationprogram:s","",true,"RemoteApp","指定 RemoteApp 的别名或可执行文件名称。有效的别名或名称。 例如，“EXCEL”。"),
        };


        public void SetRemark(int id,string remark)
        {
            using(var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                var col = db.GetCollection<Remote>();

                var model = col.FindOne(it => it.Id == id);

                model.Remark = remark;
                model.UpdateTime = DateTime.Now;

                col.Update(model);
            }
        }

        public ManageMenu Get(int id)
        {
            using(var db = new LiteDatabase(LiteDbContext.ConnectionString))
            {
                return db.GetCollection<Remote>().FindOne(it => it.Id == id);
            }
        }

    }
}
